#include "bytes_extra_ops.h"

PyObject *CPyBytes_Translate(PyObject *bytes, PyObject *table) {
    // Fast path: exact bytes object with exact bytes table
    if (PyBytes_CheckExact(bytes) && PyBytes_CheckExact(table)) {
        Py_ssize_t table_len = PyBytes_GET_SIZE(table);
        if (table_len != 256) {
            PyErr_SetString(PyExc_ValueError,
                           "translation table must be 256 characters long");
            return NULL;
        }

        Py_ssize_t len = PyBytes_GET_SIZE(bytes);
        const char *input = PyBytes_AS_STRING(bytes);
        const char *trans_table = PyBytes_AS_STRING(table);

        PyObject *result = PyBytes_FromStringAndSize(NULL, len);
        if (result == NULL) {
            return NULL;
        }

        char *output = PyBytes_AS_STRING(result);
        bool changed = false;

        // Without a loop unrolling hint performance can be worse than CPython
        CPY_UNROLL_LOOP(4)
        for (Py_ssize_t i = len; --i >= 0;) {
            char c = *input++;
            if ((*output++ = trans_table[(unsigned char)c]) != c)
                changed = true;
        }

        // If nothing changed, discard result and return the original object
        if (!changed) {
            Py_DECREF(result);
            Py_INCREF(bytes);
            return bytes;
        }

        return result;
    }

    // Fallback to Python method call for non-exact types or non-standard tables
    _Py_IDENTIFIER(translate);
    PyObject *name = _PyUnicode_FromId(&PyId_translate);
    if (name == NULL) {
        return NULL;
    }
    return PyObject_CallMethodOneArg(bytes, name, table);
}
